#include "Socket.h"
#include "winsock2.h"
#include "Exception.h"
#include <ws2tcpip.h>
SK

Socket::Socket (int addressFamily, int type, int protocol )
: af (addressFamily), type(type), protocol(protocol)
{
    this->handle = socket (addressFamily, type, protocol);

    //HACK for quickness
    if (protocol == IPPROTO_UDP)
    {
        int broadcast=1;
        setsockopt (this->handle, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof( broadcast));
    }
}

Socket::Socket (int socHandle, int af, int type, int protocol, const String& rhost)
	: handle (socHandle), af (af), type(type), protocol(protocol), rHost (rhost)
{

}

Socket::Socket (const Socket & other)
{
}

Socket::~Socket()
{
    //TODO: Cleanup
}

bool Socket::connect (const String& remoteHost, u16 port)
{
    //resolve the host
    struct addrinfo *res;
    struct addrinfo hints;
    hints.ai_family = AF_INET;
    hints.ai_protocol = IPPROTO_TCP;

    if (getaddrinfo (remoteHost.c_str(), "80", NULL, &res) != 0)
        return false;

    if (::connect (this->handle, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR)
        return false;

    freeaddrinfo (res);

    return true;
}

void Socket::bind (u16 port)
{
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons (port);
	
	int res = ::bind (this->handle, (SOCKADDR *)&addr, sizeof(addr));

	if (res == SOCKET_ERROR)
		throw new Exception ("Error binding socket!");
}

void Socket::bind (const String& ip, u16 port)
{
    sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr (ip.c_str());
	addr.sin_port = htons (port);
	
	int res = ::bind (this->handle, (SOCKADDR *)&addr, sizeof(addr));

	if (res == SOCKET_ERROR)
		throw new Exception ("Error binding socket!");
}

Socket *Socket::accept (void)
{
	sockaddr_in addr;
	
	int sze = sizeof (addr);
	int soc = ::accept (this->handle, (SOCKADDR *)&addr, &sze);

	if (soc != INVALID_SOCKET)
	{
        String rhst = inet_ntoa (addr.sin_addr);
        return new Socket (soc, af,type, protocol, rhst);
	}
	else
		return NULL;
}

void Socket::listen (u32 queueSize)
{
	::listen (this->handle, queueSize);
}

int Socket::send (byte *data, u32 bufSizeBytes)
{
	return ::send (this->handle, (char *)data, bufSizeBytes, 0);
}

void Socket::send (const String& str)
{
	int sent = 0;
	int bufSize = str.length();
	while (sent < str.length())
	{	
		sent += ::send (this->handle, (char *)&str.c_str ()[sent], bufSize, 0);
	}
}

int Socket::sendTo (byte *data, u32 bufSizeBytes, IPEndpoint& destination)
{
    sockaddr_in addr = destination.getEP();
    return ::sendto (this->handle, (char *)data, bufSizeBytes,0, (sockaddr *)&addr, sizeof (sockaddr_in));
}

void Socket::sendAll (byte *data, u32 bufSizeBytes)
{
	int sent = 0;

	while (sent < bufSizeBytes)
	{	
		sent += ::send (this->handle, (char *)&data[sent], bufSizeBytes, 0);
	}
}
	
int Socket::recv (byte *buffer, u32 bufferSize)
{
	return ::recv (this->handle, (char *)buffer, bufferSize, 0);
}

void Socket::close (void)
{
	::closesocket (this->handle);
}


void Socket::setTimeout (u32 timeoutMilli)
{
	DWORD tme = (DWORD)timeoutMilli;
    ::setsockopt (this->handle, SOL_SOCKET, SO_RCVTIMEO, (char *)tme, sizeof(DWORD));
}


String Socket::getRemoteHostIP (void)
{
    return rHost;
}

EK